package com.ljx.video.service.impl;

import cn.hutool.extra.spring.SpringUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ljx.video.constant.MyConstant;
import com.ljx.video.domain.dto.VideoTransCodingDto;
import com.ljx.video.domain.entity.File;
import com.ljx.video.domain.vo.req.FileUploadReq;
import com.ljx.video.exception.ApiException;
import com.ljx.video.mapper.VideoMapper;
import com.ljx.video.mq.constant.RabbitMqConstant;
import com.ljx.video.mq.consumer.VideoConsumer;
import com.ljx.video.mq.dto.MqTransferDto;
import com.ljx.video.mq.enums.MQTypeEnum;
import com.ljx.video.mq.producer.RabbitMQProducer;
import com.ljx.video.oss.OssService;
import com.ljx.video.service.VideoService;
import com.ljx.video.util.FileUtil;
import com.ljx.video.util.MinioUtil;
import io.minio.ComposeSource;
import org.apache.commons.compress.utils.IOUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpRange;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.swing.*;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.TimeUnit;
import java.util.regex.Matcher;
import java.util.regex.Pattern;


/**
 * @Author: LiJunXi
 * @Description:
 * @Date: 2025-01-17  23:58
 */
@Service
public class VideoServiceImpl extends ServiceImpl<VideoMapper, File> implements VideoService {

    @Autowired
    private VideoMapper videoMapper;

    @Autowired
    private OssService ossService;

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    @Autowired
    private RabbitMQProducer rabbitMQProducer;

    @Override
    public void uploadChunk(FileUploadReq req) {
        String key = MyConstant.CHUNK_FILE + req.getFileMd5();
        Double score = stringRedisTemplate.opsForZSet().score(key, req.getChunk().toString());
        if (Objects.nonNull(score)) {
            return;
        }
        ossService.uploadChunk(req);
        recordUploadChunk(key, req.getChunk());
    }

    private void recordUploadChunk(String key, Long chunk) {
        stringRedisTemplate.opsForZSet().add(key, chunk.toString(), chunk);
        stringRedisTemplate.expire(key, MyConstant.CHUNK_FILE_EXPIRE_TIME, TimeUnit.HOURS);
    }

    @Override
    public Boolean check(String fileMd5) {
        if (Objects.isNull(fileMd5)) {
            throw new ApiException("文件MD5不能为空");
        }
        return Objects.nonNull(videoMapper.selectOne(new LambdaQueryWrapper<File>()
                .eq(File::getFileMd5, fileMd5)));
    }

    @Override
    public File mergeChunk(FileUploadReq req) {
        checkMerge(req);
        File file = videoMapper.selectOne(new LambdaQueryWrapper<File>()
                .eq(File::getFileMd5, req.getFileMd5()));
        if (Objects.nonNull(file)) {
            file = new File();
            saveFileToDb(file, req);
            return file;
        }
        ossService.mergeChunk(req);
        file = new File();
        saveFileToDb(file, req);
//        rabbitMQProducer.sendMQMsg(new MqTransferDto(MQTypeEnum.VIDEO.getKey(),
//                new VideoTransCodingDto(1880613247785680898L), RabbitMqConstant.TRANS_KEY));
        return file;
    }

    private void saveFileToDb(File file, FileUploadReq req) {
        file.setFileMd5(req.getFileMd5());
        file.setFilename(req.getFilename());
        file.setFileSize(req.getFileSize());
        String extension = FileUtil.getFileExtension(req.getFilename());
        file.setFileType(SpringUtil.getBean(MinioUtil.class).getMimeType(extension));
        videoMapper.insert(file);
    }

    private void checkMerge(FileUploadReq req) {
        if (Objects.isNull(req.getFileMd5())) {
            throw new ApiException("文件MD5不能为空");
        }
        Long size = stringRedisTemplate.opsForZSet().size(MyConstant.CHUNK_FILE + req.getFileMd5());
        if (!Objects.equals(size, req.getTotalChunk())) {
            throw new ApiException("文件分片数不一致");
        }
    }


    @Override
    public void download(Long fileId, String tsNum, HttpServletRequest request, HttpServletResponse response) {
        if (Objects.isNull(fileId)) {
            throw new ApiException("文件不能为空");
        }
        File file = videoMapper.selectById(fileId);
        if (Objects.isNull(file)) {
            throw new ApiException("视频不能为空");
        }
        // 获取文件大小
        long fileSize = file.getFileSize();
        InputStream fileStream = ossService.download(file.getId(), tsNum);

        // 获取 Range 请求头
        String rangeHeader = request.getHeader("Range");
        if (rangeHeader == null || rangeHeader.isEmpty()) {
            // 如果没有 Range 请求头，则正常返回整个文件
            response.setContentType("video/mp4"); // 根据需要设置格式，支持 .m3u8、.mp4 等
            response.setHeader("Content-Disposition", "inline; filename=" + file.getFilename());

            try (InputStream fis = fileStream) {
                byte[] buffer = new byte[8192];
                int bytesRead;
                while ((bytesRead = fis.read(buffer)) != -1) {
                    response.getOutputStream().write(buffer, 0, bytesRead);
                }
                response.getOutputStream().flush();
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        } else {
            // 处理 Range 请求
            Pattern pattern = Pattern.compile("bytes=(\\d+)-(\\d*)");
            Matcher matcher = pattern.matcher(rangeHeader);
            if (matcher.find()) {
                long start = Long.parseLong(matcher.group(1));
                long end = matcher.group(2).isEmpty() ? fileSize - 1 : Long.parseLong(matcher.group(2));

                // 返回部分文件
                response.setContentType("video/mp4"); // 设置内容类型
                response.setHeader("Content-Disposition", "inline; filename=" + file.getFilename());
                response.setHeader("Content-Range", "bytes " + start + "-" + end + "/" + fileSize);
                response.setHeader("Accept-Ranges", "bytes");
                response.setStatus(HttpServletResponse.SC_PARTIAL_CONTENT);

                try (InputStream fis = fileStream) {
                    fis.skip(start); // 跳过 start 字节
                    byte[] buffer = new byte[8192];
                    int bytesRead;
                    long bytesRemaining = end - start + 1;
                    while ((bytesRead = fis.read(buffer)) != -1 && bytesRemaining > 0) {
                        long bytesToWrite = Math.min(bytesRemaining, bytesRead);
                        response.getOutputStream().write(buffer, 0, (int) bytesToWrite);
                        bytesRemaining -= bytesToWrite;
                    }
                    response.getOutputStream().flush();
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
            }
        }
    }

    @Override
    public void convertFile(Long fileId) {
        SpringUtil.getBean(VideoConsumer.class).listenTransCoding(new VideoTransCodingDto(fileId));
    }
}
